package com.cy.security.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @description:
 * @author: LiuXingYu
 * @date: 2021/7/20 16:45
 */
@Service
public class UserDetailServiceImpl implements UserDetailsService {

    /**
     * @Autowrie 注解描述属性时,注入规则是怎样的?
     * spring框架会依据@Autowrie注解描述的属性类型,从spring容器查找对应的
     * Bean,假如只找到一个则直接注入,假如找到多个还会比对属性名是否与容器中的
     * Bean的名字有相同的,有则直接注入,没有则抛出异常.
     *
     * 先通过类型注入，要求接口只能有一个实现类，否则会报错。
     * 再通过名称注入，使用@Qualifier注解标注要注入哪个实现类
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 当我们执行登录操作时,底层会通过过滤器等对象,调用这个方法.
     * @param username 这个参数为页面输出的用户名
     * @return 一般是从数据库基于用户名查询到的用户信息
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //1.基于用户名从数据库查询用户信息
        if (!"jack".equals(username)){
            throw new UsernameNotFoundException("not fond user");
        }
        //2.将用户信息封装到UserDetails对象中并返回
        //假设这个密码是从数据库查询出来的加密密码
        String password = passwordEncoder.encode("123");
        //假设这个权限信息也是从数据库查询到的
        //假如分配权限的方式是角色,编写字符串时用"ROLE_"做前缀
        List<GrantedAuthority> authorities =
                AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_admin,ROLE_normal,sys:res:retrieve,sys:res:create");
        //假设如下对象信息是从数据库查询出来的，第三个参数为用户的权限
        //这个user是SpringSecurity提供的UserDetails接口的实现,用于封装用户信息
        //后续我们也可以基于需要自己构建UserDetails接口的实现
        return new User(username, password, authorities);//这里的返回值会交给springsecurity去校验
    }
}
